package com.example.permission.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.example.permission.common.CommonParam;
import com.example.permission.common.Constants;
import com.example.permission.common.result.Result;
import com.example.permission.common.result.ResultCode;
import com.example.permission.common.utils.JwtUtil;
import com.example.permission.common.utils.Md5Util;
import com.example.permission.common.utils.NetworkUtil;
import com.example.permission.entity.po.SysAct;
import com.example.permission.entity.vo.SysAclVO;
import com.example.permission.entity.vo.SysActVO;
import com.example.permission.enums.TokenTypeEnum;
import com.example.permission.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author xuwenqian
 */
@Component
@Slf4j
public class Interceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private SysUserService sysUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String requestUrl = request.getRequestURI();
        String ipAddress = NetworkUtil.getIpAddress(request);
        log.debug("此次访问的接口：{},ip:{}", requestUrl, ipAddress);
        CommonParam.remove();
        CommonParam.getInstance().setBeginTime(new Date());
        CommonParam.getInstance().setRequestUrl(requestUrl);
        if (requestUrl.endsWith("error")) {
            return resultMessage(response, Result.fail(ResultCode.UNKNOWN.getCode(), ResultCode.UNKNOWN.getMessage()));
        }
        String token = request.getHeader("token");
        String timestamp = request.getHeader("timestamp");
        // 随机字符串
        String nonce = request.getHeader("nonce");
        String sign = request.getHeader("sign");
        // 1.必传参数
        if (StringUtils.isBlank(token) || StringUtils.isBlank(timestamp) || StringUtils.isBlank(nonce) || StringUtils.isBlank(sign)) {
            return resultMessage(response, Result.fail(ResultCode.HEADER_INVALID.getCode(), ResultCode.HEADER_INVALID.getMessage()));
        }
        // 2.请求时间间隔
        long requestInterval = System.currentTimeMillis() - Long.parseLong(timestamp);
        if (requestInterval > Constants.TIMEOUT) {
            //请求超时，请重新请求
            return resultMessage(response, Result.fail(ResultCode.SYSTEM_TIMEOUT.getCode(), ResultCode.SYSTEM_TIMEOUT.getMessage()));
        }
        // 3.校验签名
        // token + timestamp + nonce
        String signString = token + timestamp + nonce;
        String signature = Md5Util.MD5(signString);
        if (!sign.equals(signature)) {
            return resultMessage(response, Result.fail(ResultCode.SYSTEM_SIGN_ERROR.getCode(), ResultCode.SYSTEM_SIGN_ERROR.getMessage()));
        }
        // 4:校验token
        if (!checkToken(token)) {
            return resultMessage(response, Result.fail(ResultCode.TOKEN_INVALID.getCode(), ResultCode.TOKEN_INVALID.getMessage()));
        }
        // 5：校验权限
        if (!checkRoles(request)) {
            return resultMessage(response, Result.fail(ResultCode.USER_HAS_ACCESS.getCode(), ResultCode.USER_HAS_ACCESS.getMessage()));
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) {
        Date beginTime = CommonParam.getInstance().getBeginTime();
        double duration = (System.currentTimeMillis() - beginTime.getTime()) / 1000.000;
        if (duration > 3) {
            String requestUrl = request.getRequestURI();
            log.info(requestUrl + " " + " - execution time：" + String.format("%.3f", duration) + "s");
        }
    }

    /**
     * 检查token
     *
     * @param token
     * @return
     * @throws Exception
     */
    private boolean checkToken(String token) {
        //如果有传token，解密token
        Map<String, String> map = JwtUtil.parseToken(token);
        if (CollUtil.isEmpty(map)) {
            log.error("token不匹配");
            return false;
        }
        String tokenType = Convert.toStr(map.get("tokenType"));
        if (TokenTypeEnum.API.getCode().equals(tokenType)) {
            return true;
        } else if (TokenTypeEnum.USER.getCode().equals(tokenType)) {
            Long userId = Convert.toLong(map.get("userId"));
            String userNo = Convert.toStr(map.get("userNo"));
            CommonParam.getInstance().setUserId(userId);
            CommonParam.getInstance().setUserNo(userNo);
            String userRedisToken = (String) redisTemplate.opsForValue().get(Constants.USER_TOKEN + userNo);
            if (StringUtils.isBlank(userRedisToken) || !token.equals(userRedisToken)) {
                log.error("token过期");
                return false;
            }
            redisTemplate.opsForValue().set(Constants.USER_TOKEN + userNo, token, 30, TimeUnit.MINUTES);
            return true;
        }
        return false;
    }

    /**
     * 验证用户权限
     *
     * @return
     */
    private boolean checkRoles(HttpServletRequest request) {
        String userNo = CommonParam.getInstance().getUserNo();
        if (Constants.ADMIN.equals(userNo)) {
            return true;
        }
        //用户角色
        List<SysActVO> sysActVOList = sysUserService.queryRolesByUserId(CommonParam.getInstance().getUserId().intValue());
        if (CollUtil.isEmpty(sysActVOList)) {
            return false;
        }
        String roleIds = sysActVOList.stream().map(SysAct::getRoleId).map(Object::toString).collect(Collectors.joining(","));
        //用户菜单
        List<SysAclVO> sysAclVOList = sysUserService.queryMenusByRoleIds(roleIds);
        if (CollUtil.isEmpty(sysAclVOList)) {
            return false;
        }
        boolean hasAccess = false;
        String servletPath = request.getServletPath();
        for (SysAclVO sysAclVO : sysAclVOList) {
            String location = sysAclVO.getLocation();
            if (StringUtils.isNotBlank(location) && location.indexOf("?") > 0) {
                location = location.substring(0, location.indexOf("?"));
            }
            if (servletPath.equals(location)) {
                hasAccess = true;
                break;
            }
        }
        return hasAccess;
    }

    /**
     * 消息返回
     *
     * @param response
     * @return
     */
    private boolean resultMessage(HttpServletResponse response, Result result) {
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        try (ServletOutputStream writer = response.getOutputStream()) {
            writer.write(JSON.toJSONBytes(result));
        } catch (IOException e) {
            log.error("response error", e);
        }
        return false;
    }


}
